﻿using System;
using System.Threading;
using System.Collections;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace Uber.Collections
{
    /// <summary>
    /// Implements a thread-safe list with support for multiple readers and one writer
    /// </summary>
    /// <typeparam name="TKey">Values</typeparam>
    /// 
    public sealed class SafeList<TKey> : IList<TKey>
    {
        private readonly List<TKey> _inner;
        private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

        public SafeList()
        {
            _inner = new List<TKey>();
        }

        public SafeList(int capacity)
        {
            _inner = new List<TKey>(capacity);
        }

        public SafeList(SafeList<TKey> original)
        {
            _inner = new List<TKey>(original);
        }

        public void Dispose()
        {
            _lock.Dispose();
        }

        public int Count
        {
            get
            {
                using (RWLSExtension.ReadLock(_lock))
                {
                    return _inner.Count;
                }
            }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public TKey this[int index]
        {
            get
            {
                using (RWLSExtension.ReadLock(_lock))
                {
                    return _inner[index];
                }
            }
            set
            {
                using (RWLSExtension.WriteLock(_lock))
                {
                    _inner[index] = value;
                }
            }
        }

        IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator()
        {
            using (RWLSExtension.ReadLock(_lock))
            {
                return new List<TKey>(_inner).GetEnumerator();
            }
        }

        public void Add(TKey item)
        {
            using (RWLSExtension.WriteLock(_lock))
            {
                _inner.Add(item);
            }
        }

        public void Clear()
        {
            using (RWLSExtension.WriteLock(_lock))
            {
                _inner.Clear();
            }
        }

        public bool Contains(TKey item)
        {
            using (RWLSExtension.ReadLock(_lock))
            {
                return _inner.Contains(item);
            }
        }

        public void CopyTo(TKey[] array, int arrayIndex)
        {
            using (RWLSExtension.ReadLock(_lock))
            {
                _inner.CopyTo(array, arrayIndex);
            }
        }

        public bool Remove(TKey item)
        {
            using (RWLSExtension.WriteLock(_lock))
            {
                return _inner.Remove(item);
            }
        }

        public IEnumerator GetEnumerator()
        {
            using (RWLSExtension.ReadLock(_lock))
            {
                return new List<TKey>(_inner).GetEnumerator();
            }
        }

        public int IndexOf(TKey item)
        {
            using (RWLSExtension.ReadLock(_lock))
            {
                return _inner.IndexOf(item);
            }
        }

        public void Insert(int index, TKey item)
        {
            using (RWLSExtension.WriteLock(_lock))
            {
                _inner.Insert(index, item);
            }
        }

        public void RemoveAt(int index)
        {
            using (RWLSExtension.WriteLock(_lock))
            {
                _inner.RemoveAt(index);
            }
        }

        public ReadOnlyCollection<TKey> AsReadOnly()
        {
            using (RWLSExtension.ReadLock(_lock))
            {
                return new ReadOnlyCollection<TKey>(this);
            }
        }

        public void ForEach(Action<TKey> action)
        {
            using (RWLSExtension.WriteLock(_lock))
            {
                foreach (var item in _inner)
                {
                    action(item);
                }
            }
        }

        public bool Exists(Predicate<TKey> match)
        {
            using (RWLSExtension.ReadLock(_lock))
            {
                foreach (var item in _inner)
                {
                    if (match(item))
                    {
                        return true;
                    }
                }
            }

            return false;
        }
    }
}